<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>javascript插件开发</title>
</head>
<body>
    <script>



        // var a = 1;  
        // var b = 2;
        // function add(a, b) {  
        //     return a + b;
        // }
        // add(a,b); 

        // 优化：方法直接暴露在全局作用域，容易命名冲突且通过作用域查找性能消耗大。没有面向对象编程，纯程式化的函数罗列。
        // 解决方案：使用对象封装。



        // 字面量定义  
        // var person1 = {  
        //     name: "peter",  
        //     age: 18,  
        //     sayHello: function() {  
        //         alert("hello! I am " + this.name);  
        //     }  
        // };

        // 创建Object的一个实例  
        // var person2 = new Object();  
        // person2.name = "william";  
        // person2.age = 19;  
        // person2.sayHello = function() {  
        //     alert("hello! I am " + this.name);  
        // };  

        // 缺点：如果要创建多个对象，就需要重复写以上的代码。



        // function createPerson(name, age) {  
        //     var o = new Object();  
        //     o.name = name;  
        //     o.age = age;  
        //     o.sayHello = function() {  
        //         alert("hello! I am " + this.name);  
        //     };
        //     return o;
        // }  
        // var person1 = createPerson("peter", 18);  
        // var person2 = createPerson("william", 19);   

        // 优化：工厂模式已经可以满足一般我们的开发需要了，但是这个也是有点问题。命名不够直接，实现方面也可以简化。



        // var Person = function(name, age) {  
        //     this.name = name;  
        //     this.age = age;  
        //     this.sayHello = function() {  
        //         alert("hello! I am " + this.name);  
        //     }  
        // };
        // var person1 = new Person("peter", 18);  
        // var person2 = new Person("william", 19);  

        // function MathUtil() {  
        //     // 加法      
        //     this.add = function(aaa, bbb) {      
        //         var result= aaa + bbb;      
        //         alert("result == " + result);      
        //     };    
                
        //     // 减法      
        //     this.reduce = function(aaa, bbb) {      
        //         var result= aaa - bbb;      
        //         alert("result == " + result);      
        //     };     
                
        //     // 乘法      
        //     this.multiply = function(aaa, bbb) {      
        //         var result= aaa * bbb;      
        //         alert("result == " + result);      
        //     };      
                
        //     // 除法      
        //     this.divide = function(aaa, bbb) {      
        //         var result = aaa / bbb;      
        //         alert("result == " + result);      
        //     };  
        // }  

        // var mathUtil1 = new MathUtil();  
        // mathUtil1.add(1, 2);  

        // 相比较工厂模式，构造函数模式有以下不同：
        // A.没有显式创建对象
        // B.直接将属性、方法赋予了this对象
        // C.没有return

        // 这些都是因为在使用new 操作符构造函数模式时经历以下过程：
        // A.创建一个对象
        // B.将构造函数的作用域赋给新对象，所以this就指向了这个新对象
        // C.执行构造函数中的代码，例子中就是赋予新对象属性和方法
        // D.返回新对象

        // 另外一个重要的好处是：使用构造函数模式产生的对象是该函数的实例。
        // ——如果使用工厂模式，得到的person只是Object的实例。而构造函数模式得到的person却是更为具体的Person的实例！当然它同时也是Object的实例。
        // 这个优点在使用instanceof操作符进行实例判断时可以给出我们准确的判断，如果都是工厂模式出来的产品，那么检测结果就是大家都是Object的实例。

        // 优化：mathUtil函数体中的add等方法都是对象（函数也是特殊对象，定义一个函数就相当于实例化了一个对象了）。当new一个MathUtil的实例时，实例中的方法都是一样的，但是重复了。其实这个是没有必要的。



        // var MathUtil = function() {};  
        // MathUtil.prototype = {  
        //     // 将构造函数置为MathUtil，这里一定要将constructor重新设置回MathUtil，不然会指向Object的构造函数  
        //     constructor: MathUtil,  
        //     // 版本号  
        //     version: "1.0",  
        //     // 加法  
        //     add: function(aaa, bbb) {      
        //         var result= aaa + bbb;      
        //         alert("result == " + result);      
        //     },  
        //     // 减法      
        //     reduce: function(aaa, bbb) {      
        //         var result= aaa - bbb;      
        //         alert("result == " + result);      
        //     },    
        //     // 乘法      
        //     multiply: function(aaa, bbb) {      
        //         var result= aaa * bbb;      
        //         alert("result == " + result);      
        //     },    
        //     // 除法      
        //     divide: function(aaa, bbb) {      
        //         var result = aaa / bbb;      
        //         alert("result == " + result);      
        //     }  
        // };

        // var MathUtil = function() {};    
        // MathUtil.prototype = {    
        //     //省略了其他的代码  
        //     //开发人员  
        //     programmer: []   
        // };   

        // // 实例化一个MathUtil，并给programmer里增加一个开发人员姓名  
        // var m1 = new MathUtil();  
        // m1.programmer.push("william");  
        // // 实例化一个MathUtil，没有对programmer进行任何操作的情况下，m2的programmer属性无缘无故就多出来一个"william"  
        // var m2 = new MathUtil();  
        // m2.programmer;   

        // 优化：这是由于m1、m2共享了同一个原型对象导致的，而Array属于引用类型，所以就造成了m1的programmer其实和m2的programmer是同一个实例的情况。
        // 解决方案：人为地对对象的属性做个分类，如果是函数，我们就统一放到prototype属性指向的实例当中，如果只是非引用类型或者Array类型的属性我们放在构造函数里面。同时改造构造函数接受参数
    


        var MathUtil = function(version, programmer) {  
            this.name = name;  
            this.programmer = programmer;  
        };  
        MathUtil.prototye = {  
            // 将构造函数置为MathUtil，这里一定要将constructor重新设置回MathUtil，不然会指向Object的构造函数  
            constructor: MathUtil,  
            // 加法  
            add: function(aaa, bbb) {      
                var result= aaa + bbb;      
                alert("result == " + result);      
            },  
            // 减法      
            reduce: function(aaa, bbb) {      
                var result= aaa - bbb;      
                alert("result == " + result);      
            },    
            // 乘法      
            multiply: function(aaa, bbb) {      
                var result= aaa * bbb;      
                alert("result == " + result);      
            },  
            // 除法      
            divide: function(aaa, bbb) {      
                var result = aaa / bbb;      
                alert("result == " + result);      
            }  
        }

        // 引用mathUtil.js文件后使用方式：  
        var m1 = new MathUtil("william",["william"]);  
        m1.programmer.push("william2");  
        var m2 = new MathUtil("peter",["peter"]);  
        m1.programmer.push("peter2");  

        // 注意：
        // 1.当为MathUtil的prototype追加函数时，如果使用了MathUtil.prototype = {...};那么已经将MathUtil.prototype指向的对象重新定位了，这个时候constructor指向了Object，需要手动指回到MathUtil。最为保险的做法是MathUtil.prototype.add = function() {...};这样的方式来添加，这种方法没有替换MathUtil.prototype指向的实例，所以不会产生不可预料的问题。
        // 2.当MathUtil的属性是数组时，绝对不能将其定义在prototype里面，不然会有属性值覆盖问题。



        // var MathUtil = function(version, programmer) {  
        //     this.name = name;  
        //     this.programmer = programmer;  
        
        //     // 使用必要条件进行检测，然后添加函数。  
        //     if(typeof this.add != "function") {  
        
        //         // 缓存MathUtil.prototye  
        //         var proto = MathUtil.prototye;  
        
        //         // 加法  
        //         proto.add = function(aaa, bbb) {      
        //             var result = aaa + bbb;      
        //             alert("result == " + result);      
        //         };  
        
        //         // 减法  
        //         proto.reduce = function(aaa, bbb) {      
        //             var result = aaa - bbb;      
        //             alert("result == " + result);      
        //         };  
        
        //         // 乘法  
        //         proto.multiply = function(aaa, bbb) {      
        //             var result= aaa * bbb;      
        //             alert("result == " + result);      
        //         };  
        
        //         // 除法  
        //         proto.divide = function(aaa, bbb) {      
        //             var result= aaa / bbb;      
        //             alert("result == " + result);      
        //         };  
        //     }  
        // };  

        // 以上对prototype新增方法的代码在使用new操作符执行时，只会进入判断一次，然后为MathUtil.prototype追加了相应的函数方法。
        // 是否要将prototype放在和构造函数平级还是放在构造函数体内，都没有错，就看个人或者团队喜好，不过最好统一才行。

        

    </script>
</body>
</html>